"""
局部变量：就是在函数内部的变量
"""
"""
定义了方法之后，有两种传参方式
00 在局部方法中，修改全局变量 - 通过global x 指定x为全局变量，
0 函数的返回值，如何返回多个数据 如果 return x,y,z 默认返回的就是一个元组类型的数据
1 位置传递参数 #位置传参，第一个参数就是name ,第二个参数是age，第三个参数就是gender
2 关键字体传递参数 #method(name=xx,gender=xx,age=xx)
    关键字传参，没有位置的限制，更加灵活，可读性更强
3 缺省参数 def userInfo2(name,age,gender="男")
4 不定长参数
    包裹位置传递不定长参数，def xxx(*args)方法体内得到的是一个元组
    包裹关键字传递不定长参数，def xxx(**args)方法体内得到的是一个字典
    *args只能用位置传参，不能用关键字传参，
    **args只能用关键字传参，不能用位置传参
5  拆包和交换变量值
    拆包：前面提到过如果一个方法返回了多个返回值，返回的时候会把多个返回值包装为一个tuple对象
         所谓的拆包就是从tuple中把数据拿出来
    交换变量值，机会交换a b 两个变量的值，如java 中 c = a , a = b , b =c ;这样就交换了ab的值
          但是在python中，直接 a ,b =10,20 , a,b = b,a 
6 引用，id() 判断两个变量是否是同一个值的引用，可以粗暴的理解 id() 是一个变量的地址值
7 可变和不可变：
    int float str tuple  是不可变的
    list dict set 是可变的 
"""


def testA():
    a = 10  # 这个a就是一个局部变量
    print(a)


testA()

b = 100


def testB():
    b = 200  # 这里就是创建了一个新的局部变量，和外面的a没什么关系
    print(b)  # 200


testB()  # 200
print(b)  # 还是100 ，说明全局变量没有改变

c = 222


def testC():
    print(c)  # 222 这里拿到的就是全局变量c，因为局部变量中没有c，所以拿到的就是全局变量


testC()

"""
在局部方法中，修改全局变量 - 通过global x 指定x为全局变量，
"""
d = 333

def testD():
    global d  # 表示这个是全局变量d
    d = d + 1 # 全局变量d +1
    print(d) # 334
testD()
print(d) #334

def testE():
    global e
    e = 100
    return  e

testE() # 通过代码创建一个全局变量，指定全局变量为100
print(e) # 使用全局变量

"""
函数的返回值，如何返回多个数据
"""
print("""
函数的返回值，如何返回多个数据
如果 return x,y,z 默认返回的就是一个元组类型的数据
""")
def return_num(): # 指定到第一个return 就返回了，后面的代码就不跑了
    return 5
    return 6
print(return_num())

# 一个方法如何返回多个数据
def getXYZ():
    return 11,22,33
x,y,z = getXYZ()
print(x,y,z)
print(getXYZ())


print("位置传递参数")
def userInfo1(name ,age,gender):
    print(f"姓名：{name} , 年龄：{age} ， 性别：{gender}")

#位置传参，第一个参数就是name ,第二个参数是age，第三个参数就是gender
userInfo1('小明',23,'女')
userInfo1('小明','女',23) # 少一个参数就会报错

print("关键字传递参数")
userInfo1(gender="男",age="22",name="小红")

# 这里给了一个gender的默认值，如果不传递gender就是 男
print("缺省值传参")
def userInfo2(name,age,gender="男"):
    print(f"姓名：{name} , 年龄：{age} ， 性别：{gender}")
userInfo2('tom',11) # 这里没有指定gender的值，默认会用方法自带的
userInfo2('jurry',13,'女')

#不定长参数 2两种方式，包裹不定长位置参数，包裹不定长关键字参数
def sumArgs1(*nums): # 注意这里是一个 * 号，表示是位置 可变参数传值
    print(nums) #传入到方法体内部的是一个元组 (1, 2, 3, 4, 5)
sumArgs1(1,2,3,4,5)


def sumArgs2(**nums):
    print(nums) # 传入到方法体内部的是一个 字典
sumArgs2(one=1,two=2,three=3,four=4)

print("多个返回值：拆包")
def noPackage():
    return 2,"hello"
res = noPackage();
print(res) # 得到的是一个tuple
# 通过拆包的方式拿到数据
age,hel = noPackage()
print(age)
print(hel)

# 还可以对字典进行拆包
dictDemo = {
    "name":"Tom",
    "age":22,
    "address":"ShangHai"
}
a,b,c = dictDemo # 左侧变量的个数要和字典的键的个数是相同的，不同就报错
print(a,b,c) # 取到的是键的值  name age address
print(dictDemo[a] ,dictDemo[b],dictDemo[c])


print("-------- 交换a b变量的值 ----------")
# 方法一 通过中间变量实现
a1 = 10
b1 =20
print(f"交换前a1={a1},b1={b1}")
#交换值
temp = a1
a1 = b1
b1 = temp
print(f"交换后a1={a1},b1={b1}")

# 方法二通过python实现
a2,b2 = 10,20
print(f"交换前a2={a2},b2={b2}")
a2,b2 = b2,a2
print(f"交换后a2={a2},b2={b2}")

print("-------- 引用 ----------")
print("int 的引用")
ref = 1
print(f"ref的id值：{id(ref)}")
ref2 = ref
print(ref2)
print(id(ref)) # 2176547514672
print(id(ref2)) # 2176547514672

ref=2
print(f"ref的id值：{id(ref)}") # 可以发现id的值改变了
print(id(ref)) # 2210138777936 ref的引用变了
print(id(ref2)) # 但是ref2 的引用没有变，说明int类型为不可变类型

print("list列表的引用")
aa = [10,20]
bb = aa
print(aa)
print(id(aa)) # 2115121170432

aa.append(30)
print(id(aa)) #2115121170432 列表改变之后，他的id值没有变化，所以list是可以变的

print("-------- 引用当做实参 ----------")
def test1(a):
    print(a)
    print(id(a))

    a += a

    print(a)
    print(id(a))
b = 100
test1(b) # int类型计算前后id值不同 ,说明int是不可变的

c = [11,22]
test1(c) # list类型计算前后id值相同，说明list是可以改变的

s = '123'
test1(s)
